﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Common.Lib.Assembly
{
    public static class AssemblyUtil
    {
        static List<WeakReference> _assemblyCache = new List<WeakReference>();

        public static void RegisterAssembly(System.Reflection.Assembly assembly)
        {
            if (IsAssemblyRegistered(assembly))
                return;
            _assemblyCache.Add(new WeakReference(assembly));
        }

        public static System.Reflection.Assembly LoadAssembly(string assemblyName)
        {
            System.Reflection.Assembly assembly = GetRegisteredAssembly(assemblyName);
            if (assembly == null)
            {
                assembly = System.Reflection.Assembly.Load(assemblyName);
                _assemblyCache.Add(new WeakReference(assembly));
            }
            return assembly;
        }

        public static IEnumerable<string> ListAssemblyResources(System.Reflection.Assembly assembly, Func<string, bool> filter)
        {
            RegisterAssembly(assembly);
            IEnumerable<string> resNameList = assembly.GetManifestResourceNames();
            return resNameList.Where(resPath =>
                {
                    return filter(resPath);
                });
        }

        public static IEnumerable<string> ListAssemblyResources(string assemblyName, Func<string, bool> filter)
        {
            if (string.IsNullOrWhiteSpace(assemblyName))
            {
                throw new AssemblyResourceException("The given assembly name can not be null or empty");
            }

            System.Reflection.Assembly assembly = GetRegisteredAssembly(assemblyName);
            if (assembly == null)
            {
                assembly = LoadAssembly(assemblyName);
            }
            return ListAssemblyResources(assembly, filter);
        }

        private static bool IsAssemblyRegistered(System.Reflection.Assembly assembly)
        {
            return IsAssemblyRegistered(assembly.GetName().Name);
        }

        private static System.Reflection.Assembly GetRegisteredAssembly(string assemblyName)
        {
            CleanupCache();
            WeakReference wRef = _assemblyCache.SingleOrDefault(wr =>
                {
                    System.Reflection.Assembly ass = wr.Target as System.Reflection.Assembly;
                    string registeredName = ass.GetName().Name;
                    return registeredName == assemblyName;
                });
            return wRef == null ? null : wRef.Target as System.Reflection.Assembly;
        }

        private static bool IsAssemblyRegistered(string assemblyName)
        {
            return GetRegisteredAssembly(assemblyName) != null;
        }

        private static void CleanupCache()
        {
            List<WeakReference> toDeleteList = new List<WeakReference>();
            _assemblyCache.ForEach(wr =>
            {
                if (wr.Target == null || !wr.IsAlive)
                    toDeleteList.Add(wr);
            });
            toDeleteList.ForEach(wr => toDeleteList.Remove(wr));
        }
    }
}
